# Chapter 10

# Working with Multiple Pages and Data

CONTENTS

- Working with a Complex Web Site
- Creating an Application in Nested Framesets
- Working with Databases and Data
  - Using String Arrays
  - Using Associative Arrays
  - Creating Custom Data Structures
- Understanding Data Tainting
  - Enabling Data Tainting
  - Data Tainting Commands
- Working with Documents on Multiple Servers
- Maintaining State in JavaScript
- Creating a Questionnaire
- Workshop Wrap-Up
  - Next Steps
  - Q&A

In this chapter, you'll look at some of the more complex capabilities of JavaScript. You learned the basics of using frames in Chapter 9, "Using Frames, Cookies, and Other Advanced Features;" you'll look at some more sophisticated examples here. You'll also explore techniques for storing data and learn about the data tainting feature, which enables you to overcome some of Netscape's security restrictions.

## Working with a Complex Web Site

As you learned in Chapter 9 you can use nested framesets to create a complex framed document. When you combine nested framesets with JavaScript, you have to be careful about how you refer to fields in other frames.

## Creating an Application in Nested Framesets

For this section, let's return to the Fictional Software home page created in Chapter 9 turning it into a complex site using nested framesets. You'll find it looks much more like typical sites on the Web.

Let's start by creating the frameset document, shown in Listing 10.1.

> Listing 10.1. (NESTED.htm) The frameset document for the nested framesets example.

>     <HTML>
>     <FRAMESET ROWS="30%,*,52">
>     <FRAME NAME="contents" SRC="index.htm">
>     <FRAME NAME="main" SRC="fscmain2.htm">
>      <FRAMESET COLS="300,*">
>        <FRAME NAME="imagemap" SRC="map.htm">
>        <FRAME NAME="description" SRC="descrip.htm">
>      </FRAMESET>
>     </FRAMESET>
>     </HTML>

This listing uses the following frames:

- The top frame is the navigation bar created in Chapter 8 "Improving a Web Page with JavaScript."
- The next frame covers most of the page, and it is the main description of the company and links.
- The third frame is on the lower left, and it includes a client-side image map for navigation.
- The fourth frame is on the lower right. It provides a text field that will be used to display descriptions as the user moves over a link.

Listing 10.2 shows the document for the second frame, which is adapted from the version used in Chapter 9.

> Listing 10.2. (FSCMAIN2.htm) The second frame for the nested frames example.

>     <HTML>
>     <HEAD>
>     <TITLE>Fictional Software Company</TITLE>
>     </HEAD>
>     <BODY>
>     Welcome to our web page! Fictional Software Company
>     specializes in creating innovative, user-friendly software
>     applications with descriptions filled with industry
>     buzzwords.
>     <P>
>     We have a wide range of products (3 of them) to meet
>     the needs of you and your company. Follow the links
>     below for more information.
>     <P>
>     <UL>
>     <LI><A HREF="spread.htm"
>     onMouseOver="parent.description.document.form1.text1.value='Information about
>      the spreadsheet';return true;">
>     Fictional Spreadsheet 7.0</A>
>     <LI><A HREF="word.htm"
>     onMouseOver="parent.description.document.form1.text1.value='Information about
>      the word processor';return true;">
>     Fictional Word Processor 6.0</A>
>     <LI><A HREF="data.htm"
>     onMouseOver="parent.description.document.form1.text1.value='Information about
>      the database';return true;">
>     Fictional Database 7.0</A>
>     </UL>
>     <HR>
>     <I>(c)1998 FSC - designed by the FSC staff</I>

>     </BODY>
>     </HTML>

The third frame is an image map, which can be used for navigation. It uses event handlers to place informative messages in the text field in the fourth frame. Listing 10.3 shows the image map document.

> Listing 10.3. (MAP.htm) The image map document for the nested frames example.

>     <HTML>
>     <BODY>
>     <MAP NAME="map1">
>     <AREA SHAPE=RECT COORDS="6,7,61,43" HREF=support.htm
>     onMouseOver="parent.description.document.form1.text1.value='Support for our
>      products';return true;">
>     <AREA SHAPE=RECT COORDS="73,8,129,42" HREF=compinfo.htm
>     onMouseOver="parent.description.document.form1.text1.value='About our
>      Company';return true;">
>     <AREA SHAPE=RECT COORDS="140,7,200,42" HREF=order.htm
>     onMouseOver="parent.description.document.form1.text1.value='Order
>      Products';return true;">
>     <AREA SHAPE=RECT COORDS="211,6,276,43" HREF=customer.htm
>     onMouseOver="parent.description.document.form1.text1.value='Customer
>      Service';return true;">
>     <AREA SHAPE=default HREF=fscmain2.htm>
>     </MAP>
>     <IMG SRC="fscmap.gif" USEMAP="#map1">
>     </BODY>
>     </HTML>

Finally, the fourth frame contains a simple text field, which is used in place of the status bar to display descriptions as the user moves over links. Listing 10.4 shows this document.

> Listing 10.4. (DESCRIP.htm) The description text field document for the nested frames example.

>     <HTML>
>     <BODY>
>     <FORM NAME="form1">
>     <INPUT TYPE="TEXT" NAME="text1" SIZE="40" VALUE="Look Here for help.">
>     </BODY>
>     </HTML>

This is a good example of how complicated frames programming can get. Both the middle frame and the image map frame address the text field in the fourth frame to display descriptions. The final document, as displayed in Netscape, is shown in Figure 10.1.

Figure 10.1 : The nested frames example, as displayed by Netscape.

## Working with Databases and Data

One of the most talked-about issues on the Web today is the integration of databases with Web content. JavaScript isn't a very powerful language for database applications, mainly because it's limited to client-side use. However, there are some tricks you can use to store data in a database-like format in JavaScript. You'll look at several such techniques in the following sections.

### Using String Arrays

The first technique may seem simple, but it's often overlooked. Because a JavaScript variable can hold any type of data, it can be used as a string array. For example, the following code creates a three-element string array and assigns values to its elements:

>     strings = new Array(3);
>     strings[0] = "This is the first element.";
>     strings[1] = "This is the second element.";
>     strings[2] = "This is the third element.";

Each element of the array can be used as an ordinary string object. For example, this statement displays a substring from the third element of the array defined previously:

>     document.write(strings[2].substring(5,10));

### Using Associative Arrays

If you've worked with Perl, a popular language for CGI programs, you've probably used associative arrays. An associative array is an array with names instead of indexes for each of its elements.

JavaScript doesn't officially include associative arrays, but you can easily simulate them using an object. The following statements define an object and assign values to three elements:

>     animals = new Object();
>     animals["frog"] = 2;
>     animals["bear"] = 3;
>     animals["chicken"] = 4;

This technique is useful if you are storing a number of named values, especially if the names are created during the course of your program.

### Creating Custom Data Structures

One thing missing from JavaScript is two- or three-dimensional arrays. However, you can easily simulate them by creating an array of arrays. You can expand this technique to store any number of items-either numbered or named-in an array. This makes it easy to store just about anything.

Tip
The solitaire game in Chapter 15, "Real-Life Examples III," uses several of these techniques to store information about the cards in use.

## Understanding Data Tainting

In the first versions of JavaScript (Netscape 2.0 and 2.01), properties of a document were always available to JavaScript code in other documents. For example, if you loaded a page in one frame, a JavaScript program in another frame could access properties of the page-its links, anchors, and form elements.

Although reading properties of a document doesn't sound like much of a security risk, some clever folks found ways to exploit it. If there's a security risk of any kind on a Web page, you can be sure someone out there will find it and take advantage. Here are a few of the tricks that were possible due to this feature:

- A document could open a file URL, such as `file://c:\dos\`, in a frame, which would display a list of files in the frame. A JavaScript program in another frame could then read the contents (available in the `links` array) and send them to a server.
- A Web page could open an invisible frame with a JavaScript program and leave it running while you visited other sites; after watching you for a while, it could send a list of the sites you visited to its server.

Although minor, these were risks to security and privacy. The public tends to worry about such things, particularly on the Internet (and Microsoft was encouraging them to). To deter these rumors and problems, Netscape quickly released a fixed version, Netscape 2.02.

The fix in Netscape 2.02 was to prevent a document from accessing properties of another document, unless it came from the same server. Thus, if your document loaded Netscape's home page in a frame, it couldn't access the links, anchors, or even the address of the Netscape page.

Although the fixed version prevented these problems, it also removed a useful feature. If you could access properties of a document in another frame, for example, you could create a "link summary" frame with a quick reference to all the links on the page.

Luckily, Netscape found a solution to make everyone happy, beginning with Navigator 3.0b5, which introduced data tainting. This enables you to access properties of a document in another frame, but not without evidence.

As an analogy, consider the security devices used in a modern record or video store. The simple solution to prevent theft is to keep all the items in locked cabinets, but that would prevent customers from browsing them. Instead, magnetic strips are attached to each item, and can't be removed. Thus, you can take an item off the shelf and look it over-but come near the exit, and alarms go off all over the place.

Data tainting does the same thing for data from other servers. Data from another server is marked, or tainted. The data is still useful, but it is marked. No matter what you do with the data-assign it to variables, use it in calculations, and so on-it remains tainted.

When a JavaScript program attempts to send data to a server-either by submitting form data or by using an URL-it is checked for tainting. If any tainted data is present, the user is alerted and allowed to cancel the operation.

Note
To send data to a server using an URL, the application could use the data as a document name or as a parameter. In either case, a CGI script could receive the data on the server.

The actual tainting is done by using a special taint code in storing the value. The taint code is unique for each server. Thus, you can freely send data to the same server it was originally taken from, but the user is warned if you attempt to send it to a different server.

Note
When data tainting is enabled, you can also access the value of `password` objects in a form. Because their value is tainted, though, you can't send this information to a server.

### Enabling Data Tainting

To use the data tainting feature, you need to enable it using an environmental variable. The following command can be used to set the variable to enable tainting:

>     SET NS_ENABLE_TAINT=1

If you are using Windows 3.1, you can exit to DOS, type this command, then return to Windows. In Windows 95, the easiest method is to add the command to your `C:\AUTOEXEC.BAT` file, then reboot the computer. For Macintosh systems, you need to create a resource called `NS_ENABLE_TAINT`.

You can check whether the user has enabled data tainting with the `navigator.taintEnabled ( )` method.

### Data Tainting Commands

You can exercise some control over data tainting with two JavaScript functions to convert between tainted and nontainted values:

- `taint` adds taint to a value, using the current program's taint code.
- `untaint` removes the taint from a value.

These functions return a tainted or untainted result, but do not modify the original value. The main use for `untaint` is to make values available to other scripts without security restrictions.

Note
Although you can add taint to any value with `taint()`, you can only untaint values that have the current program's taint code. There is no way to remove taint from a value that originates from another window.

## Working with Documents on Multiple Servers

As an example of a multiserver application that takes advantage of data tainting, let's create a framed document that displays a link summary for an existing document-at any URL. The simplest part of this task is the frameset document, shown in Listing 10.5.

> Listing 10.5. (MULTSERV.htm) The frameset document for the link summary application.

>     <HTML>
>     <FRAMESET COLS="20%,80%">
>     <FRAME name="summary" SRC="linksumm.htm">
>     <FRAME name="destination" SRC="doc1.htm">
>     </FRAMESET>
>     </HTML>

Next, let's create the link summary document for the first frame, which will use JavaScript to display a summary of the document in the second frame. This document is shown in Listing 10.6.

> Listing 10.6. (LINKSUMM.htm) The main HTML document for the link summary JavaScript application.

>     <HTML>
>     <HEAD>
>     <TITLE>Link Summary</TITLE>
>     <SCRIPT LANGUAGE="JavaScript">
>     function newloc() {
>     // send other frame to new URL
>        parent.frames[1].location.href = document.form1.text1.value;
>     // update link summary
>        self.location.reload();
>     }
>     </SCRIPT>
>     </HEAD>
>     <BODY>
>     </BODY>
>     <H3>Link Summary</H3>
>     <HR>
>     <FORM NAME="form1">
>     <INPUT TYPE="text" NAME="text1" VALUE="enter new URL">
>     <INPUT TYPE="button" VALUE="GO" onClick="newloc();">
>     </FORM>
>     <SCRIPT LANGUAGE="JavaScript">
>     // list links in other frame
>     len = parent.frames[1].document.links.length;
>     document.write("<B>Total links: " + len + "</B>");

>     // begin numbered list
>     document.write("<BR>\n<OL>");

>     // reproduce each link here
>     for (i=0; i < len; i++) {
>        document.write("<LI><A HREF='");
>        document.write(parent.frames[1].document.links[i].href);
>        document.write("'>");
>        document.write(parent.frames[1].document.links[i].pathname);
>        document.write("</A>\n");
>     }
>     document.write("</OL>");
>     </SCRIPT>
>     <HR>
>     </HTML>

This is where the real action happens. The JavaScript functions in this document create a summary of the links in the second document. The links are listed in a numbered list, with each linked to its corresponding document name.

Of course, when you first load the document, there will be no document in the second frame. You can use the text field and form in the link summary frame to load a new document, and the link information will be displayed.

Thanks to data tainting, this should work with any Web document. It will not currently work with framed documents, because it hasn't provided for multiple frames. The output of this program, as displayed by Netscape, is shown in Figure 10.2. In the figure, I've loaded Netscape's page, and the links on the page are listed.

Figure 10.2 : The output of the link summary, application.

## Maintaining State in JavaScript

Often, your program will need to maintain state information; you may display a series of pages and need to remember something between pages. Quizzes, questionnaires, and games often need to maintain state. There are two ways to do this:

- Using cookies, which you looked at in Chapter 9
- Keeping a frame open and using its variables and functions

Both of these have their advantages and disadvantages. If your application also uses CGI, for example, you may find cookies more useful. The next task is an example of using a frame to maintain state information.

## Creating a Questionnaire

As a complex example of using frames to keep track of state between pages, let's create a questionnaire. This program asks several questions; after you answer each question, it stores the answer in an array. The array is part of the script in the top frame; the bottom frame is used to show the questions. The frameset document for this example is shown in Listing 10.7.

> Listing 10.7. (QUIZ.htm) The frameset document for the questionnaire.

>     <HTML>
>     <FRAMESET ROWS="15%,*"
>      onLoad="setTimeout('parent.MainFrame.NextQuestion();',1000);">
>     <FRAME NAME="MainFrame" SRC="quizmain.htm">
>     <FRAME NAME="QuizFrame" SRC="doc1.htm">
>     </FRAMESET>

Listing 10.8 shows the main program for the questionnaire. When you load the page, the questions are asked one at a time. After the last question, a summary of your answers is displayed. The final output of this program is shown in Figure 10.3.

Figure 10.3 : The questionnaire is complete, and the results are displayed.

> Listing 10.8. (QUIZMAIN.htm) The main JavaScript program for the quiz example.

>     <HTML>
>     <HEAD><TITLE>Questionnaire Example</TITLE>
>     <SCRIPT LANGUAGE="JavaScript">
>     // global variables
>     var answers = new Array(5);
>     var questions = new Array(5);
>     questions[0] = "What is your name";
>     questions[1] = "What is your age";
>     questions[2] = "What is your phone number";
>     questions[3] = "How many beans make 5";
>     var current = 0;
>     var quest;
>     // function to ask a question in other frame
>     function NextQuestion() {
>        if (current > 0) {
>           ans = parent.QuizFrame.document.form1.question.value;
>           answers[current-1] = ans;
>        }
>        if (current + 1 < questions.length) {
>           text = questions[current];
>           parent.QuizFrame.document.open();
>           parent.QuizFrame.document.write("<HTML><BODY>\n");
>           parent.QuizFrame.document.write("<h1>" + "Question #" + current + "</
>                      h1>");
>           parent.QuizFrame.document.write("<hr>");
>           parent.QuizFrame.document.write("<b>" + text + "?</b><br>");
>           parent.QuizFrame.document.write("<FORM NAME=\"form1\">\n");
>           parent.QuizFrame.document.write("<INPUT TYPE=\"text\" NAME=\"question\">
>                 ");
>           parent.QuizFrame.document.write("<BR><INPUT TYPE=\"BUTTON\"
>                         VALUE=\"Submit Answer\" ");
>           parent.QuizFrame.document.write("onClick=\"parent.MainFrame.NextQuestion();\"
>                         >");
>           parent.QuizFrame.document.write("</BODY></HTML>");
>           parent.QuizFrame.document.close();
>           current++;
>        }
>        else {
>           parent.QuizFrame.document.open();
>           parent.QuizFrame.document.write("<HTML><BODY>\n");
>           parent.QuizFrame.document.write("<h1>Your answers:</h1><hr>");
>           for (i=0; i<(questions.length-1); i++) {
>              parent.QuizFrame.document.write("<B>" + questions[i] + "</B>: " +
>                                answers[i] + "<BR>");
>           }
>           parent.QuizFrame.document.write("</BODY></HTML>");
>           parent.QuizFrame.document.close();
>        }
>     }

>     </SCRIPT>
>     </HEAD>
>     <BODY>
>     <H1>Questionnaire</H1>
>     </BODY>
>     </HTML>

This program uses the `questions` array to store the questions and the `answers` array to store the user's answers. When the page is loaded, the `NextQuestion()` function is called to display a question.

Each question is displayed in the bottom frame. The document you create in this frame also has a JavaScript event handler, which calls the `NextQuestion()` function (in the top frame) when the question is entered.

## Workshop Wrap-Up

In this chapter, you learned some of the more complex aspects of JavaScript and how it can work with complicated pages and data:

- How to integrate documents in nested frames and refer between them
- How to store data with string arrays and associative arrays
- How to use the data tainting feature to access properties of pages from different servers
- How to use frames to store the current state of a JavaScript application and keep track of variables between pages

### Next Steps

When you master the techniques in this chapter, you've come a long way toward becoming a JavaScript expert. Continue your studies with one of the following:

- For simpler examples of using frames in Web pages, turn to Chapter 9 "Using Frames, Cookies, and Other Advanced Features."
- For examples that illustrate the techniques you learned in this chapter, see Chapter 11, "Real-Life Examples II."
- To learn techniques for debugging JavaScript applications, see Chapter 14, "Debugging JavaScript Programs."
- To learn about using Java to further enhance your page, turn to Chapter 16, "Integrating JavaScript with Java."

### Q&A

Q: | Using data tainting, I can access properties of another document, such as links and anchors. Is there any way to read the HTML source of the document itself?
A: | Currently, there is no way to do this. The properties made available in the object hierarchy (explained in Chapter 5 "Accessing Window Elements as Objects") are all you can access via JavaScript. This is not expected to change.
Q: | Can a JavaScript program in one frame read variables (not properties) defined by a JavaScript program in another frame?
A: | Yes. Just treat the variable name as a child of the frame's window object. For example, `parent.frame1.score` refers to the `score` variable in the `frame1` frame.
Q: | What happens if the document I load into a frame is a framed document itself? Will this cause an error?
A: | This will not cause an error-in fact, it works fine. The frames of the document are created within the frame in which it is loaded. Functionally, this is treated the same as a nested frameset.